package cz.possoft.explorer.resolver;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import cz.possoft.explorer.context.FieldModifierEnum;
import cz.possoft.explorer.resolver.Resolver;

/**
 * This resolver implementation uses Getter methods to obtain the state of some object.
 *  
 * @author <a href="mailto:mposolda@gmail.com">Marek Posolda</a>
 * @version $Revision$
 */
public class GetterNamesResolverImpl<V> implements Resolver<V, String>
{

   /**
    * Get names of all fields, which have public getter attributes.<br>
    * - Don't count static and non-public modifiers<br>
    * - Don't count getters with arguments.<br>
    * 
    * @param clazz parameter is used to obtain names of getter attributes
    * @param object - This parameter is useless in GetterNamesResolverImpl implementation 
    */
   @Override
   public Map<String, Class<?>> getFields(Class<? super V> clazz, V object)
   {
      Map<String, Class<?>> mapp = new HashMap<String, Class<?>>();
      Method[] methods = clazz.getDeclaredMethods();
      for (Method m : methods)
      {
         // don't count static and non-public methods and don't count methods with arguments
         if (Modifier.isStatic(m.getModifiers()) || !(Modifier.isPublic(m.getModifiers())) || (m.getParameterTypes().length > 0))
         {
            continue;
         }
         
         if (m.getName().startsWith("get"))
         {
            String name = m.getName();
            name = name.substring(3, 4).toLowerCase() + name.substring(4);
            mapp.put(name, m.getReturnType());
         }
      }
      return mapp;
   }

   /**
    * Get name of field if field has public getter.
    */
   public Object getFieldValue(V object, String fieldName)
   {
      try
      {
         Method m = object.getClass().getMethod("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));
         return m.invoke(object);
      }
      // This can happen for classes, which are not declared as public.
      catch (IllegalAccessException iae)
      {
         System.err.println("Can't obtain value of field " + fieldName + " in object " + object + ". Cause: " + iae.getMessage());
         return "<<UNAVAILABLE>>";
      }
      catch (Exception e)
      {
         throw new RuntimeException("Can't obtain value of field " + fieldName + " in object " + object, e);
      }
   }
   
   /**
    * @return public modifier for all fields.
    */
   public FieldModifierEnum getFieldModifier(V object, String fieldName)
   {
      return FieldModifierEnum.PUBLIC;
   }
   
   public Class<String> getKeyClass()
   {
      return String.class;
   }

}
